package org.polyforms.repository.intercept;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Named;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.polyforms.repository.executor.Executor;
import org.polyforms.repository.strategy.ExecutorFindingStrategy;

/**
 * {@link org.aopalliance.intercept.Interceptor} for implementing methods in {@link org.polyforms.repository.Repository}
 * .
 * 
 * @author Kuisong Tong
 * @since 1.0
 */
@Named
public final class RepositoryInterceptor implements MethodInterceptor {
    private final Map<Method, Executor> matchedExecutors = new HashMap<Method, Executor>();
    private final List<ExecutorFindingStrategy> executorFindingStrategies;

    /**
     * Create an instance with {@link ExecutorFindingStrategy}s.
     */
    @Inject
    public RepositoryInterceptor(final List<ExecutorFindingStrategy> executorFindingStrategies) {
        this.executorFindingStrategies = executorFindingStrategies;
    }

    /**
     * {@inheritDoc}
     */
    public Object invoke(final MethodInvocation invocation) throws Throwable {
        final Method method = invocation.getMethod();
        return getExecutorWithCache(method).execute(invocation.getThis(), method, invocation.getArguments());
    }

    private Executor getExecutorWithCache(final Method method) {
        if (matchedExecutors.containsKey(method)) {
            return matchedExecutors.get(method);
        }

        final Executor executor = getExecutor(method);
        matchedExecutors.put(method, executor);
        return executor;
    }

    private Executor getExecutor(final Method method) {
        for (final ExecutorFindingStrategy executorFindingStrategy : executorFindingStrategies) {
            final Executor executor = executorFindingStrategy.getExecutor(method);
            if (executor != Executor.UNSUPPORT) {
                return executor;
            }
        }
        return Executor.UNSUPPORT;
    }
}
